/*-------------------------------------------------------------------------
 *
 * getpeereid.c
 *		获取 UNIX 域套接字连接的对等用户 ID
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/port/getpeereid.c
 *
 *-------------------------------------------------------------------------
 */

#include "c.h"

#include <sys/param.h>
#include <sys/socket.h>
#include <unistd.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_UCRED_H
#include <ucred.h>
#endif
#ifdef HAVE_SYS_UCRED_H
#include <sys/ucred.h>
#endif


/*
 * BSD风格的getpeereid()，用于缺少该函数的平台。
 */
int getpeereid(int fc_sock, uid_t *fc_uid, gid_t *fc_gid)
{
#if defined(SO_PEERCRED)
	/* Linux: 使用getsockopt(SO_PEERCRED) */
	struct ucred fc_peercred;
	socklen_t	fc_so_len = sizeof(fc_peercred);

	if (getsockopt(fc_sock, SOL_SOCKET, SO_PEERCRED, &fc_peercred, &fc_so_len) != 0 ||
		fc_so_len != sizeof(fc_peercred))
		return -1;
	*fc_uid = fc_peercred.uid;
	*fc_gid = fc_peercred.gid;
	return 0;
#elif defined(LOCAL_PEERCRED)
	/* 使用FreeBSD内核的Debian: 使用getsockopt(LOCAL_PEERCRED) */
	struct xucred peercred;
	socklen_t	so_len = sizeof(peercred);

	if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 ||
		so_len != sizeof(peercred) ||
		peercred.cr_version != XUCRED_VERSION)
		return -1;
	*uid = peercred.cr_uid;
	*gid = peercred.cr_gid;
	return 0;
#elif defined(HAVE_GETPEERUCRED)
	/* Solaris: 使用getpeerucred() */
	ucred_t    *ucred;

	ucred = NULL;				/* 必须初始化为NULL */
	if (getpeerucred(sock, &ucred) == -1)
		return -1;

	*uid = ucred_geteuid(ucred);
	*gid = ucred_getegid(ucred);
	ucred_free(ucred);

	if (*uid == (uid_t) (-1) || *gid == (gid_t) (-1))
		return -1;
	return 0;
#else
	/* 在此平台上没有可用的实现 */
	errno = ENOSYS;
	return -1;
#endif
}
